﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;

namespace Gurucore.Framework.Core.Util
{
	public class AnonymousUtil
	{
		public delegate T AnonymousActivator<T>(T sample, params object[] args);

		public static AnonymousActivator<T> GetAnonymousActivator<T>(T sample)
		{
			//TODO: cache please

			Type type = sample.GetType();
			ConstructorInfo ctor = type.GetConstructors().First();

		    ParameterInfo[] paramsInfo = ctor.GetParameters();                  

		    //create a single param of type object[]
			ParameterExpression sampleParam =
				Expression.Parameter(typeof(T), "sample");
			ParameterExpression argsParam =
		        Expression.Parameter(typeof(object[]), "args");
		 
		    Expression[] argsExp =
		        new Expression[paramsInfo.Length];            

		    //pick each arg from the params array 
		    //and create a typed expression of them
		    for (int i = 0; i < paramsInfo.Length; i++)
		    {
		        Expression index = Expression.Constant(i);
		        Type paramType = paramsInfo[i].ParameterType;              

		        Expression paramAccessorExp =
		            Expression.ArrayIndex(argsParam, index);              

		        Expression paramCastExp =
		            Expression.Convert (paramAccessorExp, paramType);              

		        argsExp[i] = paramCastExp;
		    }                  

		    //make a NewExpression that calls the
		    //ctor with the args we just created
		    NewExpression newExp = Expression.New(ctor,argsExp);                  

		    //create a lambda with the New
		    //Expression as body and our param object[] as arg
		    LambdaExpression lambda =
		        Expression.Lambda(typeof(AnonymousActivator<T>), newExp, sampleParam, argsParam);              

		    //compile it
		    AnonymousActivator<T> compiled = (AnonymousActivator<T>)lambda.Compile();
		    return compiled;
		}
	}
}
